﻿using System;

using System.Text;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
    /// </summary>
    public class IdFunctionObject : BaseFunction
    {
        public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, int arity)
        {
            if (arity < 0)
                throw new ArgumentException();

            this.idcall = idcall;
            this.tag = tag;
            this.methodId1 = id;
            this.arity = arity;
            if (arity < 0) throw new ArgumentException();
        }

        public IdFunctionObject(IdFunctionCall idcall, Object tag, int id,
                                String name, int arity, Scriptable scope)
            : base(scope, null)
        {


            if (arity < 0)
                throw new ArgumentException();
            if (name == null)
                throw new ArgumentException();

            this.idcall = idcall;
            this.tag = tag;
            this.methodId1 = id;
            this.arity = arity;
            this.functionName = name;
        }

        public void initFunction(String name, Scriptable scope)
        {
            if (name == null) throw new ArgumentException();
            if (scope == null) throw new ArgumentException();
            this.functionName = name;
            setParentScope(scope);
        }

        public bool hasTag(Object tag)
        {
            return this.tag == tag;
        }

        public int methodId()
        {
            return methodId1;
        }

        public void markAsConstructor(Scriptable prototypeProperty)
        {
            useCallAsConstructor = true;
            setImmunePrototypeProperty(prototypeProperty);
        }
        /// <summary>
        /// 做为属性加入
        /// </summary>
        /// <param name="target">目标作用域</param>
        public void addAsProperty(Scriptable target)
        {
            ScriptableObject.defineProperty(target, functionName, this,
                                            ScriptableObject.DONTENUM);
        }
        /// <summary>
        /// 导入做为父作用域的属性
        /// </summary>
        public void exportAsScopeProperty()
        {
            addAsProperty(getParentScope());
        }


        public override Scriptable getPrototype()
        {
            // Lazy initialization of prototype: for native functions this
            // may not be called at all
            Scriptable proto = base.getPrototype();
            if (proto == null)
            {
                proto = getFunctionPrototype(getParentScope());
                setPrototype(proto);
            }
            return proto;
        }


        public override Object call(Context cx, Scriptable scope, Scriptable thisObj,
                           Object[] args)
        {
            return idcall.execIdCall(this, cx, scope, thisObj, args);
        }


        public override Scriptable createObject(Context cx, Scriptable scope)
        {
            if (useCallAsConstructor)
            {
                return null;
            }
            // Throw error if not explicitly coded to be used as constructor,
            // to satisfy ECMAScript standard (see bugzilla 202019).
            // To follow current (2003-05-01) SpiderMonkey behavior, change it to:
            // return super.createObject(cx, scope);
            throw new Exception();
            // throw ScriptRuntime.typeError1("msg.not.ctor", functionName);
        }


        public override String decompile(int indent, int flags)
        {
            StringBuilder sb = new StringBuilder();
            bool justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
            if (!justbody)
            {
                sb.Append("function ");
                sb.Append(getFunctionName());
                sb.Append("() { ");
            }
            sb.Append("[native code for ");
            if (idcall is Scriptable)
            {
                Scriptable sobj = (Scriptable)idcall;
                sb.Append(sobj.getClassName());
                sb.Append('.');
            }
            sb.Append(getFunctionName());
            sb.Append(", arity=");
            sb.Append(getArity());
            sb.Append(justbody ? "]\n" : "] }\n");
            return sb.ToString();
        }


        public override int getArity()
        {
            return arity;
        }


        public override int getLength() { return getArity(); }


        public override String getFunctionName()
        {
            return (functionName == null) ? "" : functionName;
        }

        public Exception unknown()
        {

            // It is program error to call id-like methods for unknown function
            return new Exception("BAD FUNCTION ID= " + methodId1 + " MASTER=" + idcall);
        }

        private IdFunctionCall idcall;
        private Object tag;
        private int methodId1;
        private int arity;
        private bool useCallAsConstructor;
        private String functionName;
    }
}
